// PlayerImpl.cpp : implementation file
//

#include "stdafx.h"
#include "LoPlayer.h"
#include "PlayerImpl.h"

#include "..\libcore\IGraphBuilder2.h"
#include "LopSettings.h"

//////////////////////////////////////////////////////////////////////////
#define VOLUME_FULL     0L
#define VOLUME_SILENCE  -10000L
#define JIF(x) if (FAILED(hr=(x))) \
		{TRACE(TEXT("FAILED(hr=0x%x) in ") TEXT(#x) TEXT("\n\0"), hr); return hr;}

STDAPI CreateGraphBuilder2(REFCLSID, HWND, const LOP_SETTINGS*, IGraphBuilder2**);

//////////////////////////////////////////////////////////////////////////
// CPlayerImpl
//////////////////////////////////////////////////////////////////////////
CPlayerImpl::CPlayerImpl(CWnd* pWndNotify)
{
	Init();
	m_pWndNotify = pWndNotify;
}

void CPlayerImpl::Init()
{
	m_pWndNotify = NULL;
	m_bAudioOnly = FALSE;
	m_bFullscreen = FALSE;
	m_lVolume = VOLUME_FULL;
	m_PlaybackRate = 1.0;
	m_psCurrent = PS_UNUSED;
}

CPlayerImpl::~CPlayerImpl()
{
	Close ();
}

HRESULT CPlayerImpl::HandleGraphEvent( LPARAM lParam )
{
	LONG evCode = 0;
	LONG_PTR evParam1 = 0, evParam2 = 0;
	HRESULT hr = S_OK;

	// Make sure that we don't access the media event interface
	// after it has already been released.
	if (!m_pME)
		return S_OK;

	// Process all queued events
	while (SUCCEEDED(m_pME->GetEvent(&evCode, &evParam1, &evParam2, 0))) {
		// Free memory associated with callback, since we're not using it
		hr = m_pME->FreeEventParams(evCode, evParam1, evParam2);

		// If this is the end of the clip, reset to beginning
		if (EC_COMPLETE == evCode) {
			LONGLONG pos = 0;

			// Reset to first frame of movie
			hr = m_pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
			                         NULL, AM_SEEKING_NoPositioning);
			if (FAILED(hr)) {
				// Some custom filters (like the Windows CE MIDI filter)
				// may not implement seeking interfaces (IMediaSeeking)
				// to allow seeking to the start.  In that case, just stop
				// and restart for the same effect.  This should not be
				// necessary in most cases.
				if (FAILED(hr = m_pMC->Stop())) {
					TRACE(TEXT("Failed(0x%08lx) to stop media clip!\r\n"), hr);
					break;
				}

				if (FAILED(hr = m_pMC->Run())) {
					TRACE(TEXT("Failed(0x%08lx) to reset media clip!\r\n"), hr);
					break;
				}
			}
		}
	}

	return hr;
}

void CPlayerImpl::CheckVisibility(void)
{
	long lVisible;
	HRESULT hr;

	if ((!m_pVW) || (!m_pBV)) {
		// Audio-only files have no video interfaces.  This might also
		// be a file whose video component uses an unknown video codec.
		m_bAudioOnly = TRUE;
		return;
	} else {
		// Clear the global flag
		m_bAudioOnly = FALSE;
	}

	hr = m_pVW->get_Visible(&lVisible);
	if (FAILED(hr)) {
		// If this is an audio-only clip, get_Visible() won't work.
		//
		// Also, if this video is encoded with an unsupported codec,
		// we won't see any video, although the audio will work if it is
		// of a supported format.
		//
		if (hr == E_NOINTERFACE) {
			m_bAudioOnly = TRUE;
		} else {
			TRACE(TEXT("Failed(%08lx) in m_pVW->get_Visible()!\r\n"), hr);
		}
	}
}

HRESULT CPlayerImpl::InitVideoWindow( int nMultiplier, int nDivider )
{
	if (0 == nDivider)
		return E_INVALIDARG;

	LONG lHeight = 0, lWidth = 0;
	HRESULT hr = S_OK;

	if (!m_pBV)
		return S_OK;

	// Read the default video size
	hr = m_pBV->GetVideoSize(&lWidth, &lHeight);
	if (hr == E_NOINTERFACE)
		return S_OK;

	// Account for requests of normal, half, or double size
	lWidth  = lWidth  * nMultiplier / nDivider;
	lHeight = lHeight * nMultiplier / nDivider;

	m_pWndNotify->PostMessage (WM_VIDEOSIZE, 0, MAKELPARAM(lWidth, lHeight));

	return hr;
}

void CPlayerImpl::SetVideoWindowPos( LPCRECT lpRect )
{
	ASSERT(lpRect);
	if (m_pVW) {
		const RECT& rect = *lpRect;
		m_pVW->SetWindowPosition(rect.left, rect.top, rect.right, rect.bottom);
	}
}

HRESULT CPlayerImpl::PlayMovieInWindow( LPCTSTR szFile, HWND hwndVieo )
{
	if (m_pGB)
		Close();
	ASSERT(!m_pGB);
	USES_CONVERSION;
	WCHAR wFile[MAX_PATH+1];
	HRESULT hr;

	if (!szFile)
		return E_POINTER;

	LOP_SETTINGS& s = AfxGetAppSettings();
	// Clear open dialog remnants before calling RenderFile()
	m_pWndNotify->UpdateWindow();

	// Convert filename to wide character string
	wcsncpy(wFile, T2CW(szFile), NUMELMS(wFile) - 1);
	wFile[MAX_PATH] = 0;

	// Get the interface for DirectShow's GraphBuilder
	hr = CreateGraphBuilder2(LOPCSIDL_FGManagerPlayer, hwndVieo, &s, &m_pGB);
	if (SUCCEEDED(hr)) {
		// Have the graph builder construct its the appropriate graph automatically
		m_pGB->RenderFile(wFile, NULL);

		// QueryInterface for DirectShow interfaces
		JIF(m_pGB.QueryInterface(&m_pMC));
		JIF(m_pGB.QueryInterface(&m_pME));
		JIF(m_pGB.QueryInterface(&m_pMS));
		JIF(m_pGB.QueryInterface(&m_pMP));

		// Query for video interfaces, which may not be relevant for audio files
		JIF(m_pGB.QueryInterface(&m_pVW));
		JIF(m_pGB.QueryInterface(&m_pBV));

		// Query for audio interfaces, which may not be relevant for video-only files
		JIF(m_pGB.QueryInterface(&m_pBA));

		// Is this an audio-only file (no video component)?
		CheckVisibility();

		// Have the graph signal event via window callbacks for performance
		JIF(m_pME->SetNotifyWindow((OAHWND)m_pWndNotify->GetSafeHwnd(), WM_GRAPHNOTIFY, 0));

		if (m_bAudioOnly) {
			// Initialize the default player size and enable playback menu items
			// Hide video window [6/17/2008 Dean]
			InitPlayerWindow ();
		} else {
			// Setup the video window
			JIF(m_pVW->put_Owner((OAHWND)hwndVieo));
			JIF(m_pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN));

			JIF(InitVideoWindow(1, 1));
		}

		// Complete window initialization
		//CheckSizeMenu(ID_FILE_SIZE_NORMAL);
		m_pWndNotify->ShowWindow(SW_SHOWNORMAL);
		m_pWndNotify->UpdateWindow();
		m_pWndNotify->SetForegroundWindow();
		m_bFullscreen = FALSE;
		m_PlaybackRate = 1.0;
		//UpdateMainTitle();
#ifdef _DEBUG
		m_pGB->AddToROT();
#endif // _DEBUG

		// Run the graph to play the media file
		JIF(m_pMC->Run());

		SetState(PS_PLAY);
		m_pWndNotify->SetFocus();
	}

	return hr;
}

HRESULT CPlayerImpl::InitPlayerWindow(void)
{
	// Reset to a default size for audio and after closing a clip
	if (m_pWndNotify->GetSafeHwnd())
		m_pWndNotify->PostMessage (WM_VIDEOSIZE, 0, 0);

	// Check the 'full size' menu item
	//CheckSizeMenu(ID_FILE_SIZE_NORMAL);
	//EnablePlaybackMenu(FALSE, 0);

	return S_OK;
}

void CPlayerImpl::Close()
{
	HRESULT hr;

	// Stop media playback
	if (m_pMC)
		hr = m_pMC->Stop();

	// Clear global flags
	SetState(PS_STOP);
	m_bAudioOnly = TRUE;
	m_bFullscreen = FALSE;

	// Free DirectShow interfaces
	CloseInterfaces();

	// No current media state
	SetState(PS_UNUSED);

	// Reset the player window
	// UpdateMainTitle();
	InitPlayerWindow();
}

void CPlayerImpl::CloseInterfaces(void)
{
	HRESULT hr;

	// Relinquish ownership (IMPORTANT!) after hiding video window
	if (m_pVW) {
		hr = m_pVW->put_Visible(OAFALSE);
		hr = m_pVW->put_Owner(NULL);
	}

	// Disable event callbacks
	if (m_pME)
		hr = m_pME->SetNotifyWindow((OAHWND)NULL, 0, 0);

#ifdef _DEBUG
	if (m_pGB)
		m_pGB->RemoveFromROT();
#endif

	// Release and zero DirectShow interfaces
	m_pMC.Release();
	m_pME.Release();
	m_pVW.Release();
	m_pBA.Release();
	m_pBV.Release();
	m_pMS.Release();
	m_pMP.Release();
	m_pFS.Release();
	m_pGB.Release();
}

void CPlayerImpl::Pause(void)
{
	if (!m_pMC)
		return;

	// Toggle play/pause behavior
	if((GetState() == PS_PAUSE) || (GetState() == PS_STOP))
	{
		if (SUCCEEDED(m_pMC->Run()))
			SetState(PS_PLAY);
	}
	else
	{
		if (SUCCEEDED(m_pMC->Pause()))
			SetState(PS_PAUSE);
	}

	// UpdateMainTitle();
}

void CPlayerImpl::Stop(void)
{
	HRESULT hr;

	if ((!m_pMC) || (!m_pMS))
		return;

	// Stop and reset position to beginning
	if((GetState() == PS_PAUSE) || (GetState() == PS_PLAY))
	{
		LONGLONG pos = 0;
		hr = m_pMC->Stop();
		SetState(PS_STOP);

		// Seek to the beginning
		hr = m_pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
			NULL, AM_SEEKING_NoPositioning);

		// Display the first frame to indicate the reset condition
		hr = m_pMC->Pause();
	}

	// UpdateMainTitle();
}